/* A JSON handling library. Based on the JSON library by Douglas Crawford. */
if (typeof(JSON) !== "object" || JSON === null) {
	JSON = {};
}
JSON.toString = function(o) {
	switch(JSON.typeOf(o)) {
		case "object":
			return JSON.objectToString(o);
		case "array":
			return JSON.arrayToString(o);
		case "number":
			return JSON.numberToString(o);
		case "boolean":
			return JSON.booleanToString(o);
		case "date":
			return JSON.dateToString(o);
		case "string":
			return JSON.stringToString(o);
		case "null":
			return "null";
		default:
			return null;
	}
};

JSON.arrayToString = function(a) {
	if (JSON.typeOf(a) !== "array") {
		throw("Invalid Array Type");
	}
	var bits = [], i, l=a.length, js;
	
	for (i = 0; i < l; i++ ) {
		js = JSON.toString(a[i]);
		if (js !== null) {
			bits.push(js);
		}
	}

	return "[" + bits.join(",") + "]";
};
JSON.objectToString = function(o) {
	if (JSON.typeOf(o) !== "object") {
		throw("Invalid Object Type");
	}
	var bits = [], k, kjs, vjs;
	
	for (k in o) {
		if (o.hasOwnProperty(k)) {
			kjs = JSON.toString(k);
			vjs = JSON.toString(o[k]);
			if (vjs !== null && kjs !== null) {
				bits.push(kjs + ":" + vjs);
			}
		}
	}

	return "{" + bits.join(",") + "}";

};
JSON.numberToString = function(n) {
	if (JSON.typeOf(n) !== "number") {
		throw("Invalid Number Type");
	}

	return isFinite(n) ? String(n) : "null";
};
JSON.booleanToString = function(b) {
	return String(b);
};
JSON.dateToString = function(d) {
	if (JSON.typeOf(d) !== "date") {
		throw("Invalid Date Type");
	}
	
	// Format integers to have at least two digits.
	var f = function (n) {
		return n < 10 ? '0' + n : n;
	};

	return '"' + d.getFullYear() + '-' +
			f(d.getMonth() + 1) + '-' +
			f(d.getDate()) + 'T' +
			f(d.getHours()) + ':' +
			f(d.getMinutes()) + ':' +
			f(d.getSeconds()) + '"';
};
JSON.stringToString = function(s) {
	if (JSON.typeOf(s) !== "string") {
		throw("Invalid String Type");
	}

	var m = {
		'\b': '\\b',
		'\t': '\\t',
		'\n': '\\n',
		'\f': '\\f',
		'\r': '\\r',
		'"' : '\\"',
		'\\': '\\\\'
	};

	// Clean up special characters
	if (/["\\\x00-\x1f]/.test(s)) {
		return '"' + s.replace(
			/([\x00-\x1f\\"])/g,
			function (a, b) {
				var c = m[b];
				if (c) {
					return c;
				}
				c = b.charCodeAt();
				return '\\u00' + Math.floor(c / 16).toString(16) + (c % 16).toString(16);
			}
		) + '"';
	}
	return '"' + s + '"';	
};

// Parse a JSON string. Returns null if the string has a syntax error
// After parsing walks the resulting object passing each key and value to the optional
// "filter" function.  You can transform things (such as string dates to date objects) using
// a "filter" function.
JSON.parseJSON = function (s, filter) {
	var j;

	var walk = function (k, v) {
		var i;
		if (JSON.typeOf(v) === "object") {
			for (i in v) {
				if (v.hasOwnProperty(i)) {
					v[i] = walk(i, v[i]);
				}
			}
		}
		return filter(k, v);
	};


	// Parsing happens in three stages. In the first stage, we run the text against
	// a regular expression which looks for non-JSON characters. We are especially
	// concerned with '()' and 'new' because they can cause invocation, and '='
	// because it can cause mutation. But just to be safe, we will reject all
	// unexpected characters.

	if (/^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/.test(s)) {
		// In the second stage we use the eval function to compile the text into a
		// JavaScript structure. The '{' operator is subject to a syntactic ambiguity
		// in JavaScript: it can begin a block or an object literal. We wrap the text
		// in parens to eliminate the ambiguity.
		try {
			j = eval('(' + s + ')');
		} catch (e) {
			return null;
		}
	} else {
		return null;
	}

	// In the optional third stage, we recursively walk the new structure, passing
	// each name/value pair to a filter function for possible transformation.
	if (JSON.typeOf(filter) === "function") {
		j = walk('', j);
	}
	return j;
};

JSON.typeOf = function (value) {
	var s = typeof(value);
	if (s === "object") {
		// Check for null (an object can only be false if it's null
		if (value === null) {
			s = "null";
		else if (value instanceof Date) {
			s = "date";
		}
		else if (value instanceof String) {
			s = "string";
		}				
		else if (value instanceof Number) {
			s = "number";
		}
		else if (value instanceof Boolean) {
			s = "boolean";
		}
		else if (typeof value.length === "number" &&
				!(value.propertyIsEnumerable("length")) &&
				typeof value.splice === "function") {
			s = "array";
		}
	}
	return s;
};
